/*
Copyright (c) 2011, Stephane Albert
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in the
      documentation and/or other materials provided with the distribution.
    * Neither the name of the organization nor the
      names of its contributors may be used to endorse or promote products
      derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL STEPHANE ALBERT BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

#include "debridManager.h"

std::map<std::string, int> debridManager::logLevelStrMap;

debridManager::debridManager() {
    //Load default settings
    unloadConfig();
    loadlogLevelStrMap();
}

debridManager::~debridManager() {
    clearFetchers();
}

void debridManager::loadlogLevelStrMap() {
    if (logLevelStrMap.empty()) {
        logLevelStrMap["NO_LOG"]      = NO_LOG;
        logLevelStrMap["LFFAIL_LOG"]  = LFFAIL_LOG;
        logLevelStrMap["CFAIL_LOG"]   = CFAIL_LOG;
        logLevelStrMap["CMSG_LOG"]    = CMSG_LOG;
        logLevelStrMap["CALL_LOG"]    = CALL_LOG;
        logLevelStrMap["FFAIL_LOG"]   = FFAIL_LOG;
        logLevelStrMap["FMSG_LOG"]    = FMSG_LOG;
        logLevelStrMap["FALL_LOG"]    = FALL_LOG;
        logLevelStrMap["ALL_LOG"]     = ALL_LOG;
    }
}

std::string debridManager::uncipherPass(const std::string &rawPassword) {
    std::string uncipheredPass;
    if (rawPassword.compare(0, PASS_OFF, PASS_STR)) {   //No header, not ciphered
        return rawPassword;
    }
    uncipheredPass = rawPassword.substr(PASS_OFF);
    for (int i=uncipheredPass.length()-1; i >= 0; i-=PASS_XOR_LEN) {
        for (int j=0; j < PASS_XOR_LEN && i-j >= 0; ++j) {
            char& curChar = uncipheredPass.at(i-j);
            curChar ^= (char)(PASS_XOR[j] & 0x1F);
        }
    }
    return uncipheredPass;
}

std::string debridManager::cipherPass(const std::string &rawPassword) {
    if (rawPassword.empty())
        return rawPassword;
    std::string cipheredPass;
    cipheredPass.reserve(rawPassword.length() + PASS_OFF);
    cipheredPass = PASS_STR;
    cipheredPass += rawPassword;
    for (int i=cipheredPass.length()-1; i >= PASS_OFF; i-=PASS_XOR_LEN) {
        for (int j=0; j < PASS_XOR_LEN && i-j >= PASS_OFF; ++j) {
            char& curChar = cipheredPass.at(i-j);
            curChar ^= (char)(PASS_XOR[j] & 0x1F);
        }
    }
    return cipheredPass;
}

int debridManager::decodeLogLevelStr(std::string logLevelStr) {
    size_t findPos;
    int logLevel = 0;
    do {
        findPos = logLevelStr.rfind('|');
        if (findPos == std::string::npos)
            logLevel |= logLevelStrMap[logLevelStr];
        else {
            logLevel |= logLevelStrMap[logLevelStr.substr(findPos+1)];
            logLevelStr.erase(findPos);
        }
    } while (findPos != std::string::npos);
    return logLevel;
}

std::string debridManager::encodeLogLevelToStr(int logLevel) {
    bool foundLogLevel = false;
    std::string logLevelStr;
    for (int curLogLevel = FMSG_LOG; curLogLevel > 0; curLogLevel >>= 1) {
        if (curLogLevel & logLevel) {
            for (std::map<std::string, int>::const_iterator curPos = logLevelStrMap.begin(); curPos != logLevelStrMap.end(); ++curPos) {
                if (curPos->second == curLogLevel) {
                    if (foundLogLevel) {
                        logLevelStr += '|';
                    }
                    logLevelStr += curPos->first;
                    foundLogLevel = true;
                }
            }
        }
    }
    if (!foundLogLevel)
        logLevelStr = "NO_LOG";
    return logLevelStr;
}

debridFetcherBase* debridManager::findService(const std::string& serviceName) {
    if (serviceName.length() > 1) {
        if (serviceName.at(0) == 'R' || serviceName.at(0) == 'r')
            return new debridFetcherRD;
    }
    //Return AllDebrid by default to keep same beaviour as 0.4.x
    return new debridFetcherAD;
}

void debridManager::interactiveConfiguration() {
    unloadConfig();
    int nbrThreads = 0;
    configSet *curConfigSet = NULL;
    char choice;
    std::cout << _("Starting interactive configuration.") << std::endl;
    std::cout << _("\tHow many threads do you want? (Default: 1) ");
    if (isInputEmpty())
        nbrThreads = 1;
    else {
        std::cin >> nbrThreads;
        std::cin.ignore();
    }
    std::cout << _("\tDo you want to setup default settings? (Y/n) ");
    if (isInputEmpty())
        choice = 'Y';
    else {
        std::cin >> choice;
        std::cin.ignore();
    }
    if (choice == 'Y' || choice == 'y')
        curConfigSet = &defaultConfig;
    std::cout << _("\tYou can skip every field by submitting an empty line.") << std::endl;
    for (int tNbr= curConfigSet == &defaultConfig ? 0 : 1; tNbr <= nbrThreads; ++tNbr) {
        configSet newConfigSet;
        if (curConfigSet == NULL)
            curConfigSet = &newConfigSet;
        curConfigSet->maxretry = 0;
        curConfigSet->loglevel = 0;
        if (tNbr == 0)
            std::cout << _("\tConfiguring default thread.") << std::endl;
        else
            std::cout << _("\tConfiguring Thread #") << tNbr << "." << std::endl;
        std::cout << _("\tEnter username: ");
        if (!isInputEmpty())
            getline(std::cin, curConfigSet->username);
        std::cout << _("\tEnter password: ");
        if (!isInputEmpty())
            getline(std::cin, curConfigSet->password);
        std::cout << _("\tEnter service to use: ") << std::endl;
        std::cout << ("\t1) AllDebrid") << std::endl;
        std::cout << ("\t2) Real-Debrid") << std::endl;
        std::cout << _("\tChoice: ");
        if (!isInputEmpty()) {
            int servChoice;
            std::cin >> servChoice;
            switch (servChoice) {
                case 2:
                    curConfigSet->service = "RD";
                break;
                default:
                    curConfigSet->service = "AD";
                break;
            }
            clearInputNL();
        }
        std::cout << _("\tEnter logfile: ");
        if (!isInputEmpty())
            getline(std::cin, curConfigSet->logfile);
        std::cout << _("\tEnter logfailfile: ");
        if (!isInputEmpty())
            getline(std::cin, curConfigSet->logfailfile);
        std::cout << _("\tEnter monitored directory: ");
        if (!isInputEmpty())
            getline(std::cin, curConfigSet->monitdir);
        std::cout << _("\tEnter working directory: ");
        if (!isInputEmpty())
            getline(std::cin, curConfigSet->workingdir);
        std::cout << _("\tEnter done directory: ");
        if (!isInputEmpty())
            getline(std::cin, curConfigSet->donedir);
        std::cout << _("\tEnter maximum retries: ");
        if (!isInputEmpty()) {
            std::cin >> curConfigSet->maxretry;
            std::cin.ignore();
        }
        std::cout << _("\tDo you want to setup log level? (Y/n) ");
        if (isInputEmpty())
            choice = 'Y';
        else {
            std::cin >> choice;
            std::cin.ignore();
        }
        if (choice == 'Y' || choice =='y') {
            curConfigSet->loglevel = NO_LOG;
            std::cout << _("\tDo you want to log failed link to file? (Y/n) ");
            if (isInputEmpty())
                choice = 'Y';
            else {
                std::cin >> choice;
                std::cin.ignore();
            }
            if (choice == 'Y' || choice == 'y')
                curConfigSet->loglevel |= LFFAIL_LOG;
            std::cout << _("\tDo you want to log message to console? (Y/n) ");
            if (isInputEmpty())
                choice = 'Y';
            else {
                std::cin >> choice;
                std::cin.ignore();
            }
            if (choice == 'Y' || choice == 'y')
                curConfigSet->loglevel |= CMSG_LOG;
            std::cout << _("\tDo you want to log error message to console? (Y/n) ");
            if (isInputEmpty())
                choice = 'Y';
            else {
                std::cin >> choice;
                std::cin.ignore();
            }
            if (choice == 'Y' || choice == 'y')
                curConfigSet->loglevel |= CFAIL_LOG;
            std::cout << _("\tDo you want to log message to file? (Y/n) ");
            if (isInputEmpty())
                choice = 'Y';
            else {
                std::cin >> choice;
                std::cin.ignore();
            }
            if (choice == 'Y' || choice == 'y')
               curConfigSet->loglevel |= FMSG_LOG;
            std::cout << _("\tDo you want to log error message to file? (Y/n) ");
            if (isInputEmpty())
                choice = 'Y';
            else {
                std::cin >> choice;
                std::cin.ignore();
            }
            if (choice == 'Y' || choice == 'y')
                curConfigSet->loglevel |= FFAIL_LOG;
        }
        else {
            std::cout << _("\tDo you want to setup log level manualy? (Y/n) ");
            if (isInputEmpty())
                choice = 'Y';
            else {
                std::cin >> choice;
                std::cin.ignore();
            }
            if (choice == 'Y' || choice =='y') {
                std::cout << _("\tEnter loglevel value: ");
                if (!isInputEmpty()) {
                    std::cin >> curConfigSet->loglevel;
                    std::cin.ignore();
                }
            }
        }
        if (curConfigSet != &defaultConfig)
            loadedConfig.push_back(*curConfigSet);
        curConfigSet = NULL;
    }
    std::cout << _("\tDone.") << std::endl;
    std::cout << _("\tDo you want to cipher your passwords? (Y/n) ");
    if (isInputEmpty())
        choice = 'Y';
    else {
        std::cin >> choice;
        std::cin.ignore();
    }
    if (choice == 'y' || choice == 'Y') {
        defaultConfig.password = debridManager::cipherPass(defaultConfig.password);
        std::list<configSet>::const_iterator endPos = loadedConfig.end();
        for (std::list<configSet>::iterator curPos=loadedConfig.begin(); curPos!=endPos; ++curPos)
            curPos->password = debridManager::cipherPass(curPos->password);
    }
    std::cout << _("\tWhat do you want to do with this config ?") << std::endl;
    std::cout << _("\t\t[O]verwrite the current config.") << std::endl;
    std::cout << _("\t\t[A]ppend to current config.") << std::endl;
    std::cout << _("\t\t[L]oad the config in memory (will not be saved).") << std::endl;
    std::cout << _("\t\t[S]how the config (will not be saved).") << std::endl;
    std::cout << _("\to/a/L/s? ");

    if (isInputEmpty())
        choice = 'L';
    else {
        std::cin >> choice;
        std::cin.ignore();
    }

    switch (choice) {
        case 'S':
        case 's': {
            displayConfig();
            unloadConfig();
        }
        break;
        case 'O':
        case 'o': {
            std::string confPath;
            std::cout << _("\tPath to the config file (default: ./") << DEFAULT_CONFIG << "): ";
            if (isInputEmpty())
                confPath = DEFAULT_CONFIG;
            else {
                getline(std::cin, confPath);
            }
            saveConfig(confPath);
            unloadConfig();
        }
        break;
        case 'A':
        case 'a': {
            std::string confPath;
            std::cout << _("\tPath to the config file (default: ./") << DEFAULT_CONFIG << "): ";
            if (isInputEmpty())
                confPath = DEFAULT_CONFIG;
            else {
                getline(std::cin, confPath);
            }
            saveConfig(confPath, false);
            unloadConfig();
        }
        break;
        case 'L':
        case 'l':
        default:
        break;
    }
}

bool debridManager::loadConfig(const std::string& configFilePath) {
    unloadConfig();
    std::ifstream configFile(configFilePath.c_str(), std::ios_base::binary);
    if (!configFile.good())
        return false;
    std::string curLine;
    bool skipGetline = false;
    configSet *curConfSet = NULL;
    do {
        configSet newConfSet;
        if (!skipGetline)
            getline(configFile, curLine);
        else
            skipGetline = false;
        if (curLine.at(0) == '[') { //New section
            if (curLine.compare(1, 6, "global") == 0) { //Global section
                curConfSet = &defaultConfig;
            }
            else if (curLine.compare(1, 6, "thread") == 0) {
                curConfSet = &newConfSet;
                curConfSet->maxretry = 0;
                curConfSet->loglevel = 0;
            }
            do {
                getline(configFile, curLine);
                if (curLine.empty())
                    continue;
                if (curLine.at(curLine.length()-1) == '\r' || curLine.at(curLine.length()-1) == '\n' || curLine.at(curLine.length()-1) == ' ') { //Remove trash
                    curLine.erase(curLine.length()-1);
                }
                if (curLine.compare(0, 8, "username") == 0) {
                    curConfSet->username = curLine.substr(9);
                }
                else if (curLine.compare(0, 8, "password") == 0) {
                    curConfSet->password = uncipherPass(curLine.substr(9));
                }
                else if (curLine.compare(0, 7, "service") == 0) {
                    curConfSet->service = uncipherPass(curLine.substr(8));
                }
                else if (curLine.compare(0, 7, "logfile") == 0) {
                    curConfSet->logfile = curLine.substr(8);
                }
                else if (curLine.compare(0, 11, "logfailfile") == 0) {
                    curConfSet->logfailfile = curLine.substr(12);
                }
                else if (curLine.compare(0, 8, "monitdir") == 0) {
                    curConfSet->monitdir = curLine.substr(9);
                }
                else if (curLine.compare(0, 10, "workingdir") == 0) {
                    curConfSet->workingdir = curLine.substr(11);
                }
                else if (curLine.compare(0, 7, "donedir") == 0) {
                    curConfSet->donedir = curLine.substr(8);
                }
                else if (curLine.compare(0, 8, "maxretry") == 0) {
                    std::string maxRetriesNbr = curLine.substr(9);
                    for (int i = maxRetriesNbr.length(); i > 0; --i) {
                        if (!isdigit(maxRetriesNbr[i-1]))
                            continue;
                    }
                    curConfSet->maxretry = atoi(maxRetriesNbr.c_str());
                }
                else if (curLine.compare(0, 8, "loglevel") == 0) {
                    if (curConfSet->loglevel != 0)
                        curConfSet->loglevel = 0;
                    std::string logLevel = curLine.substr(9);
                    curConfSet->loglevel = decodeLogLevelStr(logLevel);
                }
            } while (!configFile.eof() && curLine[0] != '[');
            if (curConfSet != &defaultConfig) {
                loadedConfig.push_back(*curConfSet);
            }
            skipGetline = true;
        }
        curConfSet = NULL;
    } while (!configFile.eof());
    return true;
}

/*void debridManager::loadConfig(const configSet configs[], int nbrOfConfigs) {
    for (int i=0; i < nbrOfConfigs; ++i)
        loadedConfig.push_back(configs[i]);
}

void debridManager::loadDefaultConfig(const configSet &defaultConfigToLoad) {
    defaultConfig = defaultConfigToLoad;
}*/

bool debridManager::saveConfig(const std::string& configFilePath, bool overwrite) {
    std::ofstream configFile;
    if (overwrite)
        configFile.open(configFilePath.c_str(), std::ios_base::binary);
    else
        configFile.open(configFilePath.c_str(), std::ios_base::binary|std::ios_base::app);
    if (!configFile.good())
        return false;
    std::string curLine;
    configSet *curConfSet = NULL;
    curConfSet = &defaultConfig;
    std::list<configSet>::iterator curConfig = loadedConfig.begin();
    std::list<configSet>::const_iterator endConfig = loadedConfig.end();
    do {
        if (curConfSet == &defaultConfig)
            configFile << "[global]" << std::endl;
        else
            configFile << "[thread]" << std::endl;
        if (!curConfSet->username.empty())
            configFile <<  "username=" << curConfSet->username << std::endl;
        if (!curConfSet->password.empty())
            configFile <<  "password=" << curConfSet->password << std::endl;
        if (!curConfSet->service.empty())
            configFile <<  "service=" << curConfSet->service << std::endl;
        if (!curConfSet->logfile.empty())
            configFile <<  "logfile=" << curConfSet->logfile << std::endl;
        if (!curConfSet->logfailfile.empty())
            configFile <<  "logfailfile=" << curConfSet->logfailfile << std::endl;
        if (!curConfSet->monitdir.empty())
            configFile <<  "monitdir=" << curConfSet->monitdir << std::endl;
        if (!curConfSet->workingdir.empty())
            configFile <<  "workingdir=" << curConfSet->workingdir << std::endl;
        if (!curConfSet->donedir.empty())
            configFile <<  "donedir=" << curConfSet->donedir << std::endl;
        if (curConfSet->maxretry)
            configFile <<  "maxretry=" << curConfSet->maxretry << std::endl;
        if (curConfSet->loglevel)
            configFile <<  "loglevel=" << encodeLogLevelToStr(curConfSet->loglevel) << std::endl;
        if (curConfSet != &defaultConfig)
            ++curConfig;
        else
            curConfSet = &*curConfig;
        configFile << std::endl;
    } while (curConfig != endConfig);
    return true;
}

void debridManager::displayConfig() {
    std::list<configSet>::const_iterator curPos = loadedConfig.begin();
    std::list<configSet>::const_iterator endPos = loadedConfig.end();
    for (; curPos != endPos; ++curPos) {
        std::cout << _("username: ");
        if (curPos->username.empty()) {
            std::cout << defaultConfig.username;
        }
        else {
            std::cout << curPos->username;
        }
        std::cout << std::endl;
        std::cout << _("password: ");
        #ifdef DEBUG
        if (curPos->password.empty()) {
            std::cout << defaultConfig.password;
        }
        else {
            std::cout << curPos->password;
        }
        #else
        std::cout << "********";
        #endif
        std::cout << std::endl;
        std::cout << _("service: ");
        if (curPos->service.empty()) {
            std::cout << defaultConfig.service;
        }
        else {
            std::cout << curPos->service;
        }
        std::cout << std::endl;
        std::cout << _("log file: ");
        if (curPos->logfile.empty()) {
            std::cout << defaultConfig.logfile;
        }
        else {
            std::cout << curPos->logfile;
        }
        std::cout << std::endl;
        std::cout << _("log failed file: ");
        if (curPos->logfailfile.empty()) {
            std::cout << defaultConfig.logfailfile;
        }
        else {
            std::cout << curPos->logfailfile;
        }
        std::cout << std::endl;
        std::cout << _("monitor dir: ");
        if (curPos->monitdir.empty()) {
            std::cout << defaultConfig.monitdir;
        }
        else {
            std::cout << curPos->monitdir;
        }
        std::cout << std::endl;
        std::cout << _("working dir: ");
        if (curPos->workingdir.empty()) {
            std::cout << defaultConfig.workingdir;
        }
        else {
            std::cout << curPos->workingdir;
        }
        std::cout << std::endl;
        std::cout << _("done dir: ");
        if (curPos->donedir.empty()) {
            std::cout << defaultConfig.donedir;
        }
        else {
            std::cout << curPos->donedir;
        }
        std::cout << std::endl;
        std::cout << _("max retry number: ");
        if (curPos->maxretry == 0) {
            std::cout << defaultConfig.maxretry;
        }
        else {
            std::cout << curPos->maxretry;
        }
        std::cout << std::endl;
        std::cout << _("loglevel: ");
        int logLevel;
        if (curPos->loglevel == 0) {
            logLevel = defaultConfig.loglevel;
        }
        else {
            logLevel = curPos->loglevel;
        }
        std::cout << encodeLogLevelToStr(logLevel);
        std::cout << std::endl;
    }
}

void debridManager::unloadConfig() {
    loadedConfig.clear();
    defaultConfig.username.clear();
    defaultConfig.password.clear();
    defaultConfig.service.clear();
    defaultConfig.logfile     = "log.txt";
    defaultConfig.logfailfile = "failed.txt";
    defaultConfig.monitdir    = "monit";
    defaultConfig.workingdir  = "working";
    defaultConfig.donedir     = "done";
    defaultConfig.maxretry    = 5;
    defaultConfig.loglevel    = CALL_LOG|FALL_LOG|LFFAIL_LOG;
}

bool debridManager::start() {
    if (!fetcherList.empty()) {
        std::cerr << _("Debrid fetcher list isn't empty, some fetcher should be working.") << std::endl;
        return false;
    }
    if (defaultConfig.monitdir.empty()) {   //Prevent bad things such as deleting files in current directory
        std::cerr << _("Fatal error no monitoring directory, exiting.") << std::endl;
        return false;
    }
    if (loadedConfig.empty()) {
        //Try to spawn a thread with the global settings
        if (!defaultConfig.username.empty() && !defaultConfig.password.empty()) {
            debridFetcherBase *newFetcher = findService(defaultConfig.service);
            newFetcher->setLogFile(defaultConfig.logfile);
            newFetcher->setFailedLogFile(defaultConfig.logfailfile);
            newFetcher->setLogLevel(defaultConfig.loglevel);
            newFetcher->setMonitorDir(defaultConfig.monitdir);
            newFetcher->setWorkingDir(defaultConfig.workingdir);
            newFetcher->setDoneDir(defaultConfig.donedir);
            newFetcher->setMaxRetries(defaultConfig.maxretry);
            if (!newFetcher->authClient(defaultConfig.username, defaultConfig.password)) {
                std::cerr << _("Unable to authenticate client.") << std::endl;
                delete newFetcher;
            }
            else {
                std::cout << _("Starting new thread.") << std::endl;
                newFetcher->start();
                fetcherList.push_back(newFetcher);
                return true;
            }
        }
        else {
            std::cerr << _("No credentials found, exiting.") << std::endl;
        }
        return false;
    }
    std::list<configSet>::const_iterator curPos = loadedConfig.begin();
    std::list<configSet>::const_iterator endPos = loadedConfig.end();
    for (; curPos != endPos; ++curPos) {
        if ((curPos->username.empty() && defaultConfig.username.empty()) || (curPos->password.empty() && defaultConfig.password.empty())) {
            std::cerr << _("Thread loaded but no credentials found, skipping.") << std::endl;
            continue;
        }
        debridFetcherBase *newFetcher = findService(curPos->service.empty() ? defaultConfig.service : curPos->service);
        newFetcher->setLogFile(curPos->logfile.empty() ? defaultConfig.logfile : curPos->logfile);
        newFetcher->setFailedLogFile(curPos->logfailfile.empty() ? defaultConfig.logfailfile : curPos->logfailfile);
        newFetcher->setLogLevel(curPos->loglevel == 0 ? defaultConfig.loglevel : curPos->loglevel);
        newFetcher->setMonitorDir(curPos->monitdir.empty() ? defaultConfig.monitdir : curPos->monitdir);
        newFetcher->setWorkingDir(curPos->workingdir.empty() ? defaultConfig.workingdir : curPos->workingdir);
        newFetcher->setDoneDir(curPos->donedir.empty() ? defaultConfig.donedir : curPos->donedir);
        newFetcher->setMaxRetries(curPos->maxretry == 0 ? defaultConfig.maxretry : curPos->maxretry);
        if (!newFetcher->authClient(curPos->username.empty() ? defaultConfig.username : curPos->username,\
                                    curPos->password.empty() ? defaultConfig.password : curPos->password)) {
            std::cerr << _("Unable to authenticate client.") << std::endl;
            delete newFetcher;
        }
        else {
            std::cout << _("Starting new thread.") << std::endl;
            newFetcher->start();
            fetcherList.push_back(newFetcher);
        }
    }
    return !fetcherList.empty();
}

void debridManager::clearFetchers() {
    std::list<debridFetcherBase *>::iterator curPos = fetcherList.begin();
    std::list<debridFetcherBase *>::iterator endPos = fetcherList.end();
    for (; curPos != endPos; ++curPos) {
        delete *curPos;
    }
    fetcherList.clear();
}
